package network.link;

import java.util.Vector;

/**
 * Static class that handles framing frames algorithms
 * <p>
 * Frame separator is MIN MAX MAX MAX MIN. Bit stuffing stuffs
 * MIN after 2 MAXes.
 * 
 */

public class Frame {
  
  private final static byte[] TERMINATION_SEQ = {Byte.MIN_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MAX_VALUE, Byte.MIN_VALUE};
  
  /**
   * Checks whether a sequence of bytes has the terminating sequence as its suffix
   * @requires sequence not null
   * @return true iff sequence has as its terminating sequence its suffix
   */
  public static boolean isTerminated(Vector<Byte> sequence) {
    assert (sequence != null);
    if (sequence.size() < TERMINATION_SEQ.length) {
      return false;
    }
    for (int i = 0; i < TERMINATION_SEQ.length; ++i) {
      if (sequence.get(sequence.size() - (TERMINATION_SEQ.length - i)) != TERMINATION_SEQ[i]) {
        return false;
      }
    }
    return true;
  }
  
  /**
   * Unmarshals a sequence of bytes from its framing informations
   * @requires sequence not null
   * @return the unmarshalled sequence
   */
  public static byte[] unmarshall(Vector<Byte> sequence) {
    assert (sequence != null);
    assert (isTerminated(sequence));
    
    Vector<Byte> unmarshalled_vec = new Vector<Byte>();
    int cnt = 0;
    for (int i = 0; i < sequence.size() - TERMINATION_SEQ.length; ++i) {
      if (cnt == 2) {
        cnt = 0;
      } else {
        unmarshalled_vec.add(sequence.get(i));
      }
      if (sequence.get(i) == Byte.MAX_VALUE) {
        ++cnt;
      }
    }
    
    byte[] ret = new byte[unmarshalled_vec.size()];
    for (int i = 0; i < unmarshalled_vec.size(); ++i) {
      ret[i] = unmarshalled_vec.get(i);
    }
    return ret;
  }
  
  /**
   * Marshals a sequence of bytes
   * @requires sequence not null
   * @param sequence the message
   * @return the marshalled message
   */
  
  public static byte[] marshall(byte[] sequence) {
    Vector<Byte> ret = new Vector<Byte>();
    int cnt = 0;
    for (int i = 0; i < sequence.length; ++i) {
      ret.add(sequence[i]);
      if (sequence[i] == Byte.MAX_VALUE) {
        ++cnt;
      }
      if (cnt == 2) {
        cnt = 0;
        ret.add(Byte.MIN_VALUE);
      }
    }
    for (int i = 0; i < TERMINATION_SEQ.length; ++i) {
      ret.add(TERMINATION_SEQ[i]);
    }
    
    byte[] ret_bytes = new byte[ret.size()];
    for (int i = 0; i < ret.size(); ++i) {
      ret_bytes[i] = ret.get(i);
    }
    return ret_bytes;
  }
  
}
